package dmxGroupControl.ui;

import dmxGroupControl.*;
import dmxGroupControl.listeners.Listener;
import dmxGroupControl.listeners.Mapping;
import dmxGroupControl.listeners.MidiMessageMapping;
import dmxGroupControl.listeners.OscMessageMapping;
import dmxGroupControl.solvers.*;
import dmxGroupControl.transmitters.*;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.jdom2.Element;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;


public class UniverseEditWindowController
{
    @FXML public CheckBox chkPan;
    @FXML public TextField txtPanMin;
    @FXML public TextField txtPanMax;
    @FXML public CheckBox chkTilt;
    @FXML public TextField txtTiltMin;
    @FXML public TextField txtTiltMax;

    @FXML private TextField txtSolverFakeStrobeHoldFrames;
    @FXML private CheckBox chkSolverFakeStrobe;

    @FXML private TreeView<String> trvUniverses;
	@FXML private Button btnAddItem;
	@FXML private Button btnRemoveItem;
	@FXML private Button btnAddChannel;
	@FXML private Button btnRemoveChannel;
	@FXML private VBox vbxChannels;
	@FXML private CheckBox chkSolverHSB;
	@FXML private CheckBox chkSolverMasterAtFull;
	@FXML private CheckBox chkSolverLookAt;
	@FXML private Button btnUniverseSave;
	
	@FXML private TitledPane tlpFixture;
    @FXML private ComboBox<String> cboFixtureID;
    @FXML private TextField txtStartChannel;
    @FXML private ComboBox<String> cboFixtureType;
    @FXML private Button btnFixtureDefinitionSave;
    @FXML private Button btnFixtureDefinitionLoad;
    @FXML private Button btnFixtureDefinitionDelete;
	
	@FXML private TextField txtLookAtX;
	@FXML private TextField txtLookAtY;
	@FXML private TextField txtLookAtZ;
	
	@FXML private TitledPane tlpUniverse;
	@FXML private TextField txtUniverseName;

    @FXML private TitledPane tlpGroup;
    @FXML private TextField txtGroupName;
    @FXML private CheckBox chkSolverSimpleChase;
    @FXML private TextField txtSolverSimpleChaseMaxDistance;
    @FXML private TitledPane tlpGroupSolvers;
    
    @FXML private VBox vbxMainVBox;
    @FXML private Label lblLastValue;
	
    private ArrayList<TitledPane> allPanes;
    
	private Fixture currentFixture;
	private Group currentGroup;
	private Universe currentUniverse;
	private TreeItem<String> currentItem;

	private String[] channelNames = new String[]  { "Red", "Green", "Blue", "Master", "Strobe"	};
	
	// the initialize method is automatically invoked by the FXMLLoader - it's
	// magic
	public void initialize()
	{
	    LoadTreeItems();
        LoadFixtureNames();
        LoadPanes();
		
		txtStartChannel.setOnAction(
                event ->
                {
                    int channel = Integer.parseInt(txtStartChannel.getText());
                    ((DMXFixture)currentFixture).SetStartChannel(channel);
                    currentItem.setValue(currentFixture.toString());
                }
        );

	    txtLookAtX.setOnAction(
                event ->
                {
                    LookAtSolver solver = (LookAtSolver)currentFixture.GetSolver("lookAt");
                    solver.SetSource(
                            Float.parseFloat(txtLookAtX.getText()),
                            Float.parseFloat(txtLookAtY.getText()),
                            Float.parseFloat(txtLookAtZ.getText())
                    );
                }
         );

        txtSolverFakeStrobeHoldFrames.setOnAction(
            event ->
            {
                FakeStrobeSolver solver = (FakeStrobeSolver)currentFixture.GetSolver("fakeStrobe");
                solver.SetInterval(Integer.parseInt(txtSolverFakeStrobeHoldFrames.getText()));
            }
        );
		
		txtLookAtY.setOnAction(txtLookAtX.getOnAction());
		txtLookAtZ.setOnAction(txtLookAtX.getOnAction());
		
		txtSolverSimpleChaseMaxDistance.setOnAction(
                event ->
                {
                    SimpleChaseSolver solver = (SimpleChaseSolver)currentGroup.GetSolver("simpleChase");
                    solver.SetMaxDistance(Integer.parseInt(txtSolverSimpleChaseMaxDistance.getText()));
                }
        );
		
		btnAddItem.setOnAction(
                event ->
                {
                    TreeItem<String> newParent = currentItem;
                    TreeNodeLevel level = TreeNodeLevel.GetTreeNodeLevel(trvUniverses.getTreeItemLevel(currentItem));

                    switch(level)
                    {
                    case CONTROLLER_NODE_VALUE:
                        Universe newUniverse = new Universe("new Universe");
                        LightController.GetInstance().GetUniverses().add(newUniverse);
                        TreeItem<String> newUniverseItem = new TreeItem<>(newUniverse.toString());
                        newParent.getChildren().add(newUniverseItem);
                        newUniverseItem.getChildren().add(new TreeItem<>("Groups"));
                        break;
                    case UNIVERSE_NODE_VALUE:
                        Group newGroup = new Group("new Group");
                        currentUniverse.GetGroups().add(newGroup);
                        newParent.getChildren().add(new TreeItem<>(newGroup.toString()));
                        break;
                    case GROUP_NODE_VALUE:
                        DMXFixture newFixture = new DMXFixture(cboFixtureID.getValue(), Integer.parseInt(txtStartChannel.getText()));
                        Channel channel = new Channel("Master", 0, 255, newFixture.GetChannelStart());
                        txtStartChannel.setText(newFixture.GetChannelEnd() + "");
                        newFixture.GetChannels().Add(channel);
                        currentGroup.GetFixtures().add(newFixture);
                        newParent.getChildren().add(new TreeItem<>(newFixture.toString()));
                        break;
                    default:
                    }
                }
        );
		
		btnRemoveItem.setOnAction(
            event ->
            {
                TreeNodeLevel level = TreeNodeLevel.GetTreeNodeLevel(trvUniverses.getTreeItemLevel(currentItem));

                switch(level)
                {
                    case UNIVERSE_NODE_VALUE:
                        LightController.GetInstance().GetUniverses().remove(currentUniverse);
                        break;
                    case FIXTURE_NODE_VALUE:
                        currentGroup.GetFixtures().remove(currentFixture);
                        break;
                    case GROUP_NODE_VALUE:
                        currentUniverse.GetGroups().remove(currentGroup);
                        break;
                    default:
                        return;
                }
                currentItem.getParent().getChildren().remove(currentItem);
            }
        );
		
		btnUniverseSave.setOnAction(event -> LightController.GetInstance().SaveUniverses());
		
		btnAddChannel.setOnAction(
            event ->
            {
                int min = 0;
                int currentDMXChannel = 1;
                if(vbxChannels.getChildren().size() > 0)
                {
                    HBox last = (HBox)vbxChannels.getChildren().get(vbxChannels.getChildren().size() - 1);
                    TextField previousChannel = (TextField)last.getChildren().get(0);
                    TextField previousMax = (TextField)last.getChildren().get(last.getChildren().size() - 1);
                    int previousChannelInt = Integer.parseInt(previousChannel.getText());
                    int previousMaxInt = Integer.parseInt(previousMax.getText());
                    if(previousMaxInt < 255)
                    {
                        currentDMXChannel = previousChannelInt;
                        min = previousMaxInt;
                    }
                    else
                    {
                        currentDMXChannel = previousChannelInt + 1;
                    }
                }

                Channel channel = new Channel("Untitled", min, 255, currentDMXChannel + ((DMXFixture)currentFixture).GetChannelStart() - 1);
                currentFixture.GetChannels().Add(channel);
                vbxChannels.getChildren().add(GetChannelRow(((DMXFixture)currentFixture).GetChannelStart(), channel));
                currentItem.setValue(currentFixture.toString());
            }
        );
		
		btnRemoveChannel.setOnAction(
            event ->
            {
                HBox last = (HBox)vbxChannels.getChildren().get(vbxChannels.getChildren().size() - 1);
                vbxChannels.getChildren().remove(last);
                Channel lastChannel = currentFixture.GetChannels().GetLast();
                currentFixture.GetChannels().Remove(lastChannel);
                currentItem.setValue(currentFixture.toString());
            }
        );
		
		cboFixtureID.setOnAction(
            event ->
            {
                if (!cboFixtureID.isDisabled())
                {
                    currentFixture.SetName(cboFixtureID.getValue());
                    currentItem.setValue(currentFixture.toString());
                }
            }
        );

        cboFixtureType.valueProperty().addListener(
            (observable, oldValue, newValue) ->
            {
                if(!cboFixtureType.isDisabled())
                {
                    currentFixture.SetType(FixtureType.Parse(cboFixtureType.getValue()));
                }
            }
        );
		        
        cboFixtureID.valueProperty().addListener(
            (observable, oldValue, newValue) ->
            {
                boolean exists = LightController.GetFixtureDefinition(newValue) != null;
                if(exists)
                {
                    btnFixtureDefinitionSave.setText("Overwrite");
                    btnFixtureDefinitionLoad.setDisable(false);
                }
                else
                {
                    btnFixtureDefinitionSave.setText("Save New");
                    btnFixtureDefinitionLoad.setDisable(true);
                }
            }
        );
        
        btnFixtureDefinitionDelete.setOnAction(
            event ->
            {
                LightController.GetInstance().DeleteFixtureDefinition(cboFixtureID.getValue());
                LoadFixtureNames();
            }
        );
		        
        btnFixtureDefinitionLoad.setOnAction(
            event ->
            {
                Element fixtureDefinition = LightController.GetFixtureDefinition(cboFixtureID.getValue());
                if(fixtureDefinition != null)
                {
                    currentFixture.GetChannels().Clear();
                    currentFixture.GetChannels().LoadFromFixtureDefinition(((DMXFixture)currentFixture).GetChannelStart(), fixtureDefinition);
                    currentFixture.SetName(cboFixtureID.getValue());
                    currentItem.setValue(currentFixture.toString());
                    SetEnabledPane(null);
                    SelectFixture(currentFixture);
                    SetEnabledPane(tlpFixture);
                }
            }
        );
		
        btnFixtureDefinitionSave.setOnAction(
            event ->
            {
                LightController.GetInstance().SaveFixtureDefinition(currentFixture);
                LoadFixtureNames();
            }
        );

        chkSolverFakeStrobe.selectedProperty().addListener(
            (observableValue, oldItem, newItem) ->
            {
                txtSolverFakeStrobeHoldFrames.setDisable(!chkSolverFakeStrobe.isSelected());
                if(!tlpFixture.isDisabled())
                {
                    if(newItem && !oldItem && !currentFixture.HasSolver("fakeStrobe"))
                    {
                        currentFixture.GetSolvers().add(
                            new FakeStrobeSolver(
                                currentFixture,
                                Integer.parseInt(txtSolverFakeStrobeHoldFrames.getText())
                            )
                        );
                    }
                    else if(oldItem && !newItem && currentFixture.HasSolver("fakeStrobe"))
                    {
                        currentFixture.RemoveSolver("fakeStrobe");
                    }
                }
            }
        );
		
        chkSolverSimpleChase.selectedProperty().addListener(
            (observableValue, oldItem, newItem) ->
            {
                txtSolverSimpleChaseMaxDistance.setDisable(!chkSolverSimpleChase.isSelected());
                if(!chkSolverSimpleChase.isDisabled())
                {
                    if(newItem && !oldItem && !currentGroup.HasSolver("simpleChase"))
                    {
                        currentGroup.AddSimpleChaseSolver(Integer.parseInt(txtSolverSimpleChaseMaxDistance.getText()));
                    }
                    else if(oldItem && !newItem && currentGroup.HasSolver("simpleChase"))
                    {
                        currentGroup.RemoveSolver("simpleChase");
                    }
                }
            }
        );
        
		chkSolverHSB.selectedProperty().addListener(
            (observableValue, oldItem, newItem) ->
            {
                if(!chkSolverHSB.isDisabled())
                {
                    if(newItem && !oldItem && !currentFixture.HasSolver("hsb"))
                    {
                        currentFixture.GetSolvers().add(new HSBSolver(currentFixture));
                    }
                    else if(oldItem && !newItem && currentFixture.HasSolver("hsb"))
                    {
                        currentFixture.RemoveSolver("hsb");
                    }
                }
            }
        );
		
		chkSolverMasterAtFull.selectedProperty().addListener(
            (observableValue, oldItem, newItem) ->
            {
                if(!chkSolverMasterAtFull.isDisabled())
                {
                    if(newItem && !oldItem && !currentFixture.HasSolver("masterAtFull"))
                    {
                        currentFixture.GetSolvers().add(new MasterAtFullSolver(currentFixture));
                    }
                    else if(oldItem && !newItem && currentFixture.HasSolver("masterAtFull"))
                    {
                        currentFixture.RemoveSolver("masterAtFull");
                    }
                }
            }
        );
		
		chkSolverLookAt.selectedProperty().addListener(
            (observableValue, oldItem, newItem) ->
            {
                if(!chkSolverLookAt.isDisabled())
                {
                    txtLookAtX.setDisable(false);
                    txtLookAtY.setDisable(false);
                    txtLookAtZ.setDisable(false);

                    if(newItem && !oldItem && !currentFixture.HasSolver("lookAt"))
                    {
                        currentFixture.GetSolvers().add(
                            new LookAtSolver(
                                Double.parseDouble(txtLookAtX.getText()),
                                Double.parseDouble(txtLookAtY.getText()),
                                Double.parseDouble(txtLookAtZ.getText()),
                                currentFixture
                            )
                        );
                    }
                    else if(oldItem && !newItem && currentFixture.HasSolver("lookAt"))
                    {
                        currentFixture.RemoveSolver("lookAt");
                    }
                }
            }
        );

		txtGroupName.setOnAction(
            event ->
            {
                currentGroup.SetName(txtGroupName.getText());
                currentItem.setValue(currentGroup.toString());
            }
        );
		
		txtUniverseName.setOnAction(
            event ->
            {
                currentUniverse.SetName(txtUniverseName.getText());
                currentItem.setValue(currentUniverse.toString());
            }
        );
		
		trvUniverses.getSelectionModel().selectedItemProperty().addListener(
            (observableValue, oldItem, newItem) ->
            {
                currentItem = newItem;

                if(newItem != null)
                {
                    TreeNodeLevel level = TreeNodeLevel.GetTreeNodeLevel(trvUniverses.getTreeItemLevel(currentItem));
                    UpdateTreeItemSelection(newItem, level);
                }
            }
        );
	}
	
	private void LoadPanes()
    {
        allPanes = new ArrayList<>();
        allPanes.addAll(Arrays.asList(tlpFixture, tlpGroup, tlpUniverse));
        SetActivePane(null);
    }

    private void UpdateTreeItemSelection(TreeItem<String> newItem, TreeNodeLevel nodeLevel)
    {
        String universeName, groupName;
        Universe universe = null;
        Group group = null;
        Fixture fixture = null;
        Listener listener = null;
        Transmitter transmitter = null;
        
        SetEnabledPane(null);
        
        switch(nodeLevel)
        {
        case FIXTURE_NODE_VALUE:
            universeName = newItem.getParent().getParent().getValue();
            groupName = newItem.getParent().getValue();
            
            universe = LightController.GetInstance().FindUniverse(universeName);
            group = universe.FindGroup(groupName);
            fixture = group.FindFixture(newItem.getValue());
            
            SelectFixture(fixture);
            
            SetActivePane(tlpFixture);
            
            btnRemoveChannel.setDisable(fixture.GetChannels().size() == 0);
            break;
        case GROUP_NODE_VALUE:
            universeName = newItem.getParent().getValue();
            universe = LightController.GetInstance().FindUniverse(universeName);

            groupName = newItem.getValue();
            group = universe.FindGroup(groupName);

            txtGroupName.setText(group.GetName());
            chkSolverSimpleChase.setSelected(group.HasSolver("simpleChase"));
            if(group.HasSolver("simpleChase"))
            {
                txtSolverSimpleChaseMaxDistance.setText(((SimpleChaseSolver)group.GetSolver("simpleChase")).GetMaxDistance() + "");
            }
            else
            {
                txtSolverSimpleChaseMaxDistance.setText("2");
            }
            SetActivePane(tlpGroup);

            break;
        case UNIVERSE_NODE_VALUE:
            universeName = newItem.getValue();
            universe = LightController.GetInstance().FindUniverse(universeName);
            
            SetActivePane(tlpUniverse);
            break;
        default:
            SetActivePane(null);
            break;
        }
        UpdateAddRemoveButton(nodeLevel);
        
        currentGroup = group;
        currentUniverse = universe;
        currentFixture = fixture;
    }
	
	private void SelectFixture(Fixture fixture)
    {
	    currentFixture = fixture;
	    EnableDisableSolvers();
        cboFixtureID.setValue(fixture.GetName());
        cboFixtureType.setValue(fixture.GetFixtureType().GetFriendlyName());
        if(fixture instanceof DMXFixture)
        {
        	txtStartChannel.setText(((DMXFixture)fixture).GetChannelStart() + "");
        }
        vbxChannels.getChildren().clear();
        
        for(Channel channel : fixture.GetChannels())
        {
            vbxChannels.getChildren().add(GetChannelRow(((DMXFixture)fixture).GetChannelStart(), channel));
        }

        chkSolverHSB.setSelected(fixture.HasSolver("hsb"));
        chkSolverMasterAtFull.setSelected(fixture.HasSolver("masterAtFull"));
        chkSolverLookAt.setSelected(fixture.HasSolver("lookAt"));
        if(fixture.HasSolver("fakeStrobe"))
        {
            FakeStrobeSolver solver = (FakeStrobeSolver)fixture.GetSolver("fakeStrobe");
            chkSolverFakeStrobe.setSelected(true);
            txtSolverFakeStrobeHoldFrames.setText(solver.GetInterval() + "");
            txtSolverFakeStrobeHoldFrames.setDisable(false);
        }
        else
        {
            chkSolverFakeStrobe.setSelected(false);
            txtSolverFakeStrobeHoldFrames.setDisable(true);
        }
    }
    
    private void UpdateAddRemoveButton(TreeNodeLevel nodeLevel)
    {
        switch(nodeLevel)
        {
        case CONTROLLER_NODE_VALUE:
            btnAddItem.setText("Add Universe");
            btnAddItem.setDisable(false);
            btnRemoveItem.setText("Remove");
            btnRemoveItem.setDisable(true);
            break;
        case UNIVERSE_NODE_VALUE:
            btnAddItem.setText("Add Group");
            btnAddItem.setDisable(false);
            btnRemoveItem.setText("Remove Universe");
            btnRemoveItem.setDisable(false);
            break;
        case GROUP_NODE_VALUE:
            btnAddItem.setText("Add Fixture");
            btnAddItem.setDisable(false);
            btnRemoveItem.setText("Remove Group");
            btnRemoveItem.setDisable(false);
            break;
        case FIXTURE_NODE_VALUE:
            btnAddItem.setText("Add");
            btnRemoveItem.setText("Remove Fixture");
            btnAddItem.setDisable(true);
            btnRemoveItem.setDisable(false);
            break;
        }
    }

    private void EnableDisableSolvers()
    {
        chkSolverHSB.setDisable(!HSBSolver.GetIsApplicableToFixture( currentFixture ));
        chkSolverMasterAtFull.setDisable(!MasterAtFullSolver.GetIsApplicableToFixture( currentFixture ));
        chkSolverLookAt.setDisable(!LookAtSolver.GetIsApplicableToFixture(currentFixture)) ;
        chkSolverFakeStrobe.setDisable(!FakeStrobeSolver.GetIsApplicableToFixture(currentFixture)) ;
    }
    
	private void SetActivePane(TitledPane activePane)
    {
        SetEnabledPane(activePane);
        SetVisiblePane(activePane);
    }
    
    private void SetEnabledPane(TitledPane activePane)
    {
        for(TitledPane pane : allPanes)
        {
            pane.setDisable(activePane != pane);
        }
    }
    
    private void SetVisiblePane(TitledPane activePane)
    {
        vbxMainVBox.getChildren().clear();
        if(activePane != null)
        {
            vbxMainVBox.getChildren().add(activePane);
            activePane.setExpanded(true);
        }
    }

    private void LoadFixtureNames()
	{
	    cboFixtureID.getItems().clear();
	    cboFixtureID.getItems().addAll(LightController.GetInstance().GetFixtureNames());
	}
	
	// loads some strings into the tree in the application UI.
	private void LoadTreeItems()
	{
		LightController controller = LightController.GetInstance();
		TreeItem<String> root = new TreeItem<>(controller.toString());
		
        for(Universe universe : controller.GetUniverses())
		{
			TreeItem<String> universeItem = new TreeItem<>(universe.toString());
			root.getChildren().add(universeItem);
			universeItem.setExpanded(true);

			for(Group group : universe.GetGroups())
			{
				TreeItem<String> groupItem = new TreeItem<>(group.toString());
                universeItem.getChildren().add(groupItem);
				for(Fixture fixture : group.GetFixtures())
				{
					TreeItem<String> fixtureItem = new TreeItem<>(fixture.toString());
					groupItem.getChildren().add(fixtureItem);
				}
			}
		}
        
		root.setExpanded(true);
		trvUniverses.setRoot(root);
	}
	
	private HBox GetMappingRow(Mapping mapping)
	{
		HBox mappingRow = new HBox();
		Insets defaultMargin = new Insets(0, 0, 0, 8);
		Insets rowMargin = new Insets(0, 0, 8, 0);
		
		mappingRow.getStyleClass().add("mappingBox");
		
		Label addressLabel = new Label("CC / Address");
		
		TextField mappingAddress = new TextField(mapping.GetPattern());
		mappingAddress.getStyleClass().add("mappingAddress");
		mappingAddress.textProperty().addListener((ov, old_val, new_val) -> mapping.SetPattern(new_val));
		
		Label channelLabel = new Label("Attribute");
		
		TextField mappingChannel = new TextField(mapping.GetAttribute());
		mappingChannel.getStyleClass().add("mappingChannel");
		
		mappingChannel.textProperty().addListener((ov, old_val, new_val) -> mapping.SetAttribute(new_val));
		
		mappingRow.getChildren().add(addressLabel);
		mappingRow.getChildren().add(mappingAddress);
		mappingRow.getChildren().add(channelLabel);
		mappingRow.getChildren().add(mappingChannel);
		
		HBox.setMargin(addressLabel, defaultMargin);
		HBox.setMargin(mappingAddress, defaultMargin);
		HBox.setMargin(channelLabel, defaultMargin);
		HBox.setMargin(mappingChannel, defaultMargin);
		
		HBox.setHgrow(mappingAddress, Priority.ALWAYS);
		HBox.setHgrow(mappingChannel, Priority.ALWAYS);
		VBox.setMargin(mappingRow, rowMargin);
		
		return mappingRow;
	}
	
	private HBox GetChannelRow(int startChannel, Channel channel)
	{
		HBox channelRow = new HBox();
		Insets defaultMargin = new Insets(0, 0, 0, 8);
		Insets rowMargin = new Insets(0, 0, 8, 0);
		
		TextField channelNumber = new TextField(channel.GetDMXChannel() - startChannel + 1 + "");
		channelNumber.getStyleClass().add("channelNumber");
		channelNumber.textProperty().addListener(
            (ov, old_val, new_val) -> channel.SetDMXChannel(startChannel + Integer.parseInt(new_val) - 1)
        );
		
		Slider slider = new Slider(channel.GetMin(), channel.GetMax(), channel.GetDMXValue());
		slider.getStyleClass().add("channelValueSlider");
		slider.setPadding(defaultMargin);
        slider.setTooltip(new Tooltip(Math.round(slider.getValue()) + ""));
		
		slider.valueProperty().addListener(
            (ov, old_val, new_val) ->
            {
                double range = slider.getMax() - slider.getMin();
                double value = (new_val.doubleValue() - slider.getMin()) / range;
                channel.SetValue((float)value);
                lblLastValue.setText(channel.GetDMXValue() + "");
                slider.setTooltip(new Tooltip(Math.round(slider.getValue()) + ""));
            }
        );
		
		TextField channelMin = new TextField(channel.GetMin() + "");
		channelMin.getStyleClass().add("channelValueMinMax");
		
		channelMin.textProperty().addListener(
            (ov, old_val, new_val) ->
            {
                slider.setMin(Double.parseDouble(new_val));
                channel.SetMin(Integer.parseInt(new_val));
            }
        );
		
		TextField channelMax = new TextField(channel.GetMax() + "");
		channelMax.getStyleClass().add("channelValueMinMax");
		
		channelMax.textProperty().addListener(
            (ov, old_val, new_val) ->
            {
                slider.setMax(Double.parseDouble(new_val));
                channel.SetMax(Integer.parseInt(new_val));
            }
        );
		
		ComboBox<String> channelName = new ComboBox<>();
		channelName.setValue(channel.GetName());
		channelName.setEditable(true);
		channelName.getStyleClass().add("channelName");
		channelName.getItems().addAll(channelNames);
				
		channelName.valueProperty().addListener((ov, old_val, new_val) -> channel.SetName(new_val));
		
		channelRow.getStyleClass().add("channelBox");
		
		channelRow.getChildren().add(channelNumber);
		channelRow.getChildren().add(slider);
		channelRow.getChildren().add(channelName);
		channelRow.getChildren().add(channelMin);
		channelRow.getChildren().add(channelMax);
		
		HBox.setMargin(slider, defaultMargin);
		HBox.setMargin(channelName, defaultMargin);
		HBox.setMargin(channelMin, defaultMargin);
		HBox.setMargin(channelMax, defaultMargin);
		VBox.setMargin(channelRow, rowMargin);
		
		return channelRow;
	}
}
